package com.ruoyi.learn.java.design.pattern.behavior;

/**
 * 观察者模式 (Observer Pattern) 示例：实现新闻发布与订阅。
 */
public class ObserverPatternDemo {

    //region ========== 1. 观察者 (Observer) ==========

    /**
     * 观察者接口
     * 定义了所有观察者必须实现的更新方法。
     * 当主题状态改变时，会调用此方法。
     */
    public interface Observer {
        /**
         * 更新方法，由主题调用。
         * @param news 新的新闻内容
         */
        void update(String news);
    }

    //endregion

    //region ========== 2. 主题 (Subject) ==========

    /**
     * 主题接口
     * 定义了管理观察者和通知观察者的方法。
     */
    public interface Subject {
        /**
         * 注册观察者
         * @param observer 要注册的观察者
         */
        void registerObserver(Observer observer);

        /**
         * 注销观察者
         * @param observer 要注销的观察者
         */
        void removeObserver(Observer observer);

        /**
         * 通知所有已注册的观察者
         */
        void notifyObservers();
    }

    //endregion

    //region ========== 3. 具体主题 (ConcreteSubject) ==========

    /**
     * 具体主题：新闻机构
     * 维护一个观察者列表，并在新闻发布时通知它们。
     */
    public static class NewsAgency implements Subject {
        // 使用集合存储所有注册的观察者
        private java.util.List<Observer> observers;
        // 新闻内容
        private String news;

        public NewsAgency() {
            this.observers = new java.util.ArrayList<>();
            this.news = "";
        }

        @Override
        public void registerObserver(Observer observer) {
            if (observer != null && !observers.contains(observer)) {
                observers.add(observer);
                System.out.println("  > 【新闻机构】用户 '" + getObserverName(observer) + "' 订阅成功！");
            }
        }

        @Override
        public void removeObserver(Observer observer) {
            observers.remove(observer);
            System.out.println("  > 【新闻机构】用户 '" + getObserverName(observer) + "' 取消订阅。");
        }

        @Override
        public void notifyObservers() {
            System.out.println("  > 【新闻机构】正在向 " + observers.size() + " 位订阅者推送新闻...");
            for (Observer observer : observers) {
                observer.update(this.news); // 调用每个观察者的 update 方法
            }
            System.out.println("  > 【新闻机构】新闻推送完成。\n");
        }

        /**
         * 业务方法：发布新闻
         * 这是主题状态改变的触发点。
         * @param news 新闻内容
         */
        public void setNews(String news) {
            this.news = news;
            System.out.println("\n=== 新闻发布 ===");
            System.out.println("  > 【新闻机构】发布了新新闻: \"" + news + "\"");
            // 状态改变后，立即通知所有观察者
            notifyObservers();
        }

        // 辅助方法：获取观察者的名字（用于日志）
        private String getObserverName(Observer observer) {
            return observer.getClass().getSimpleName().replace("User", "");
        }
    }

    //endregion

    //region ========== 4. 具体观察者 (ConcreteObserver) ==========

    /**
     * 具体观察者：用户
     * 实现了 Observer 接口，存储对主题的引用。
     */
    public static class User implements Observer {
        private String name;
        private NewsAgency newsAgency; // 持有对主题的引用，以便在 update 时获取数据

        public User(String name, NewsAgency newsAgency) {
            this.name = name;
            this.newsAgency = newsAgency;
            // 在构造时自动注册为观察者
            newsAgency.registerObserver(this);
        }

        /**
         * 实现观察者接口的 update 方法
         * 定义收到通知后的具体行为（这里是显示新闻）。
         * @param news 从主题接收到的新闻
         */
        @Override
        public void update(String news) {
            System.out.println("    > 【用户 " + name + "】收到了新闻: \"" + news + "\"");
            // 这里可以执行更复杂的逻辑，比如保存到本地、发送邮件等
            displayNews(news);
        }

        /**
         * 显示新闻的方法
         * @param news 新闻内容
         */
        private void displayNews(String news) {
            System.out.println("      > 【显示】" + name + " 正在阅读: \"" + news + "\"");
        }

        /**
         * 用户可以取消订阅
         */
        public void unsubscribe() {
            if (newsAgency != null) {
                newsAgency.removeObserver(this);
                // 可选：断开与主题的连接
                this.newsAgency = null;
            }
        }
    }

    //endregion

    //region ========== 5. 主函数 (Main) - 演示观察者模式 ==========

    public static void main(String[] args) {
        System.out.println("=== 观察者模式演示 ===\n");

        // 1. 创建主题（新闻机构）
        System.out.println("1. 创建新闻机构...");
        NewsAgency agency = new NewsAgency();

        // 2. 创建观察者（用户）并自动注册
        System.out.println("2. 创建用户并订阅新闻...");
        User userAlice = new User("Alice", agency);
        User userBob = new User("Bob", agency);
        User userCharlie = new User("Charlie", agency);

        // 3. 演示新闻发布和自动通知
        System.out.println("3. 演示新闻发布与自动通知...\n");

        // --- 发布第一则新闻 ---
        agency.setNews("Java 17 正式发布，带来新特性！");

        // --- 发布第二则新闻 ---
        agency.setNews("观察者模式详解，Java 实现！");

        // 4. 演示动态注册和注销
        System.out.println("4. 演示动态订阅与取消订阅...\n");

        // Bob 取消订阅
        System.out.println("  > 【用户 Bob】决定取消订阅新闻。");
        userBob.unsubscribe();

        // 发布第三则新闻，Bob 不应该收到
        agency.setNews("新用户 David 加入，Bob 已取消订阅。");

        // 创建新用户 David (稍后订阅)
        User userDavid = new User("David", agency);

        // 发布第四则新闻，所有当前订阅者（Alice, Charlie, David）都应该收到
        agency.setNews("欢迎 David 加入我们的读者群！");

        // 5. 观察者模式的优势
        System.out.println("--- 观察者模式的优势 ---");
        System.out.println("• **松耦合**: 主题 (NewsAgency) 和观察者 (User) 之间是松耦合的。");
        System.out.println("  主题只知道观察者实现了 Observer 接口，");
        System.out.println("  不关心观察者的具体实现。");
        System.out.println("• **可复用**: 具体主题和具体观察者可以独立复用。");
        System.out.println("  例如，NewsAgency 也可以通知 'EmailNotifier' 或 'SMSNotifier'。");
        System.out.println("• **可扩展**: 可以随时增加新的观察者 (如 AppUser, WebUser)，");
        System.out.println("  只需实现 Observer 接口并注册，无需修改 NewsAgency 的代码。");
        System.out.println("  符合开闭原则。");
        System.out.println("• **支持广播通信**: 一个主题可以通知多个观察者。");

        // 6. 注意事项
        System.out.println("\n--- 注意事项 ---");
        System.out.println("• **内存泄漏风险**: 如果观察者在注销时没有正确从主题列表中移除，");
        System.out.println("  或者主题持有观察者的强引用，可能导致观察者无法被垃圾回收。");
        System.out.println("  在 Java 中，可以考虑使用 WeakReference。");
        System.out.println("• **通知顺序**: notifyObservers() 的顺序依赖于集合的遍历顺序，");
        System.out.println("  通常是注册的顺序，但不保证。如果顺序很重要，需要特殊处理。");
        System.out.println("• **循环依赖**: 要避免主题和观察者之间形成循环引用。");
        System.out.println("• **性能**: 如果观察者数量巨大或 update() 操作耗时，");
        System.out.println("  notifyObservers() 可能会阻塞主题。可以考虑异步通知。");
        System.out.println("• **意外更新**: 如果主题频繁改变状态，可能会导致观察者被频繁通知，");
        System.out.println("  造成不必要的开销。可以增加条件判断，仅在必要时通知。");

        // 7. 与发布-订阅模式的区别
        System.out.println("\n--- 与发布-订阅模式 (Publish-Subscribe) 的区别 ---");
        System.out.println("• **观察者模式**: 通常是**同步**的，主题直接调用观察者的 update()。");
        System.out.println("  主题和观察者通常在**同一个进程**内，关系紧密。");
        System.out.println("• **发布-订阅模式**: 通常是**异步**的，通过一个**中间件 (Message Broker)**");
        System.out.println("  （如 Kafka, RabbitMQ）进行消息传递。发布者和订阅者完全解耦，");
        System.out.println("  可以在**不同的进程或机器**上。发布者不知道订阅者的存在。");
        System.out.println("  (观察者模式可以看作是发布-订阅模式的一种简化、同步、进程内实现)");

        // 8. Java 内置支持
        System.out.println("\n--- Java 内置支持 ---");
        System.out.println("• Java 提供了 java.util.Observable 类和 java.util.Observer 接口。");
        System.out.println("  但由于 Observable 是一个类（不是接口），");
        System.out.println("  使用它会限制你的具体主题类的继承（Java 单继承），");
        System.out.println("  因此现代 Java 开发更推荐使用自定义接口（如本例）或");
        System.out.println("  更现代的事件驱动框架（如 Spring Event, Reactor）。");
    }

    //endregion
}
